<template>
  <!-- 矢量数据弹窗 -->
  <div class="dialog-box layer-dialog" v-loading="isloadLayer" element-loading-text="正在加载..."
    element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
    <div class="box-content">
      <div class="content-header">
        <span>添加 {{ loadLayerForm.serverType }} {{ selectNode.label }}</span>
      </div>

      <div class="content-body">
        <el-form ref="layerForm" :model="loadLayerForm" :rules="loadLayerRules" label-width="80px">
          <el-form-item label="服务名称">
            <el-input v-model="loadLayerForm.serverName" placeholder="请输入服务名称"></el-input>
          </el-form-item>

          <el-form-item label="服务地址" prop="serverUrl" class="layer-server-box">
            <el-input v-model="loadLayerForm.serverUrl" :placeholder="layerPlaceholder"></el-input>
            <el-input class="layer-url-hint" v-model="layerPlaceholder" :disabled="true"></el-input>
          </el-form-item>

          <div class="origin-title">
            <span>定位点</span>
          </div>

          <div class="position-box">
            <el-form-item label="经度" prop="lng">
              <el-input v-model="loadLayerForm.lng" placeholder="请输入经度"></el-input>
            </el-form-item>

            <el-form-item label="纬度" prop="lat">
              <el-input v-model="loadLayerForm.lat" placeholder="请输入纬度"></el-input>
            </el-form-item>

            <el-form-item label="高度" prop="height">
              <el-input v-model="loadLayerForm.height" placeholder="请输入高度"></el-input>
            </el-form-item>
          </div>

          <div class="position-box">
            <el-form-item label="偏航角" prop="heading">
              <el-input v-model="loadLayerForm.heading" placeholder="请输入偏航角"></el-input>
            </el-form-item>

            <el-form-item label="俯仰角" prop="pitch">
              <el-input v-model="loadLayerForm.pitch" placeholder="请输入俯仰角"></el-input>
            </el-form-item>

            <el-form-item label="翻滚角" prop="roll">
              <el-input v-model="loadLayerForm.roll" placeholder="请输入翻滚角"></el-input>
            </el-form-item>
          </div>

          <el-form-item class="radio-type">
            <el-radio v-model="loadLayerForm.serverType" v-for="(item, index) in layerServerTypeList" :key="index"
              :label="item" @input="getPlaceholder(item)">{{ item }}服务</el-radio>
          </el-form-item>

          <el-form-item class="submit-btn">
            <el-button @click="closeLayerDialog">取消</el-button>
            <el-button type="primary" @click="clickConfirmBtn">确定</el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

<script>
import wkt from "wkt";
import * as turf from "@turf/turf";
import Layer from '@/components/layoutSubassemb/js/layer.js';
import { validateServerUrl, validateLngRange, validateLatRange, validateHeightRange, validateHeading, validatePitch, validateRoll, } from '@/assets/js/rules.js';
export default {
  props: {
    selectNode: {
      type: Object,
      default: {}
    }
  },

  data () {
    return {
      isloadLayer: false,
      loadLayerForm: {
        serverName: '',
        serverUrl: '',
        serverType: 'WMTS',
        lng: '',
        lat: '',
        height: '',
        heading: '',
        pitch: '',
        roll: ''
      },
      loadLayerRules: {
        serverUrl: { validator: validateServerUrl, trigger: 'blur' },
        lng: { validator: validateLngRange, trigger: 'blur' },
        lat: { validator: validateLatRange, trigger: 'blur' },
        height: { validator: validateHeightRange, trigger: 'blur' },
        heading: { validator: validateHeading, trigger: 'blur' },
        pitch: { validator: validatePitch, trigger: 'blur' },
        roll: { validator: validateRoll, trigger: 'blur' }
      },
      layerPlaceholder: '/mapserver/{type}/WMTS/{version}/{serverName}/{styleId}/getTile/{TileMatrix}/{TileRow}/{TileCol}',
      layerServerTypeList: ['WMTS', 'WMS', 'XYZ']
    };
  },

  watch: {
    selectNode: {
      handler (newVal, oldVal) {
        const newLabel = (newVal && newVal.label) ? newVal.label : '';
        const oldLabel = (oldVal && oldVal.label) ? oldVal.label : '';
        if (newLabel != oldLabel) {
          this.getPlaceholder(this.loadLayerForm.serverType);
        }
      },
      deep: true
    }
  },

  methods: {
    // 根据服务类型返回提示字
    getPlaceholder (type) {
      const label = this.selectNode.label || '矢量数据';
      const apiType = label == '矢量数据' ? 'mapserver' : 'rasterserver';
      this.layerServerTypeList = label == '矢量数据' ? ['WMTS', 'WMS', 'XYZ'] : ['WMTS', 'XYZ'];
      switch (type) {
        case 'WMTS':
          this.layerPlaceholder = `/${apiType}/{type}/WMTS/{version}/{serverName}${label == '矢量数据' ? '/{styleId}' : ''}/getTile/{TileMatrix}/{TileRow}/{TileCol}`;
          break;

        case 'WMS':
          this.layerPlaceholder = `/mapserver/{type}/WMS/{version}/{serverName}/{styleId}/{request}`;
          break;

        case 'XYZ':
          this.layerPlaceholder = label == '矢量数据' ? `/${apiType}/{type}/{severName}/getMap?styleId={styleId}&x={TileCol}&y={TileRow}&l={TileMatrix}&ratio={r}&titlesize={titlesize}` : `/${apiType}/{type}/{severName}/{TileMatrix}/{TileCol}/{TileRow}/getMap`;
          break;
        default:
          break;
      }
    },

    // 确定按钮
    clickConfirmBtn () {
      try {
        this.$refs.layerForm.validate(async (valid) => {
          if (!valid) return;
          this.isloadLayer = true;
          const { id, label } = this.selectNode;
          const parentNode = this.$parent.getCurNodeData(id);
          const controlItem = parentNode.control.find(e => { return (e.name == '显示'); });

          const resourceId = this.$guid();
          let serverName = '';
          const serverType = this.loadLayerForm.serverType;
          const serverUrl = this.loadLayerForm.serverUrl.replace(/\s*/g, "");
          const show = controlItem.icon == '隐藏_s.png' ? false : true;
          let position = {};
          let isExist = null;
          const setStyle = null;
          const whiteFilmStretching = null;
          const layerInfo = this._Layer.getServerUrlInfo(serverType, serverUrl);
          const { isStandard, serverIpPort, apiType, dataType, tilesize, testUrl } = layerInfo;

          if (!isStandard) {
            this.$parent.message(`${serverType}服务地址格式错误，请检查`);
            this.isloadLayer = false;
            return;
          }
          if (label == '矢量数据' && apiType != 'mapserver') {
            this.$parent.message(`请输入矢量数据地址`);
            this.isloadLayer = false;
            return;
          } else if (label == '影像数据' && apiType != 'rasterserver') {
            this.$parent.message(`请输入影像数据地址`);
            this.isloadLayer = false;
            return;
          }

          position = {
            lng: Number(this.loadLayerForm.lng.replace(/\s*/g, "")),
            lat: Number(this.loadLayerForm.lat.replace(/\s*/g, "")),
            height: Number(this.loadLayerForm.height.replace(/\s*/g, "")),
            heading: Number(this.loadLayerForm.heading.replace(/\s*/g, "")) || 0,
            pitch: Number(this.loadLayerForm.pitch.replace(/\s*/g, "")) || -90,
            roll: Number(this.loadLayerForm.roll.replace(/\s*/g, "")) || 0
          };
          const { lng, lat, height } = position;
          if (!lng || !lat || !height) {
            const posData = await this.getServerPosition(serverIpPort, serverType, serverUrl);
            if (!posData) {
              this.$message.error('无法获取当前数据中心点，请手动输入')
              this.isloadLayer = false;
              return
            }
            Object.assign(position, posData);
          }
          const isFirst = this.$parent.isFirstUpdataNode();
          isExist = await this.$parent.fileExist(testUrl);
          this.isloadLayer = false;

          const fromName = this.loadLayerForm.serverName.replace(/\s*/g, "");
          if (fromName) {
            serverName = fromName;
            parentNode.children.forEach(item => {
              if (item.label == fromName) {
                const labelList = parentNode.children.map(e => { return e.label; });
                serverName = this.$parent.unitListName(labelList, fromName);
              }
            });
          } else {
            const defaultName = label == '矢量数据' ? '矢量' : '影像';
            serverName = `我的${defaultName}_${parentNode.children.length + 1}`;
          }

          this.closeLayerDialog();
          /**
           * @param {resourceId} 标识符
           * @param {serverName} 服务名称
           * @param {serverType} 服务类型（WMTS、WMS、XYZ）
           * @param {serverIpPort} ip端口
           * @param {serverUrl} 服务url
           * @param {isStandard} 是否符合标准
           * @param {isFirst} 是否第一次加载
           * @param {apiType} api类型（rasterserver、mapserver）
           * @param {dataType} 数据类型（image、layer、vmap、label）
           * @param {tilesize} 瓦片大小
           * @param {show} 服务显隐
           * @param {position} 图层定位
           * @param {isExist} 服务是否存在
           * @param {setStyle} 样式设置
           * */
          const loadOption = { resourceId, serverName, serverType, serverIpPort, serverUrl, isStandard, isFirst, apiType, dataType, tilesize, show, position, isExist, setStyle };
          if (isExist) {
            this._Layer.loadLayer(loadOption);
          }
          this.$emit('appendLayerNode', loadOption);
        });
      } catch (error) {
        this.isloadLayer = false;
      }
    },

    clearLayerForm () {
      for (let k in this.loadLayerForm) {
        this.loadLayerForm[k] = k == 'serverType' ? this.loadLayerForm[k] : '';
      }
    },

    closeLayerDialog () {
      this.clearLayerForm();
      this.$emit('close');
    },

    // 获取服务定位
    async getServerPosition (serverIpPort, serverType, serverUrl) {
      const { serverName, layerName } = this.getServerInfo(serverType, serverUrl);
      let posData = null
      switch (serverType) {
        case 'WMTS':
          const wmtsXml = await this.getCapabilities(serverUrl, 'WMTS');
          const wmtsCenter = this.getWMTSBoundingBox(wmtsXml);
          const wmtsHeight = await this.getHeight([wmtsCenter]);
          posData = {
            lng: wmtsCenter[0],
            lat: wmtsCenter[1],
            height: wmtsHeight + 1000
          };
          break;
        case 'WMS':
          const wmsXml = await this.getCapabilities(serverUrl, 'WMS');
          const wmsCenter = this.getWMSBoundingBox(wmsXml)
          const wmsHeight = await this.getHeight([wmsCenter]);
          posData = {
            lng: wmsCenter[0],
            lat: wmsCenter[1],
            height: wmsHeight + 1000
          };
          break;
        case 'XYZ':
          const xyzCenter = await this.getXYZCenter(serverIpPort, serverName, layerName)
          if (xyzCenter) {
            const xyzHeight = await this.getHeight([xyzCenter]);
            posData = {
              lng: xyzCenter[0],
              lat: xyzCenter[1],
              height: xyzHeight + 1000
            };
          }
          break;
        default:
          break;
      }
      return posData
    },
    /**
     * 获取服务的xml信息，并验证xml格式的正确性
     *
     * @param serverUrl 服务地址
     * @param serviceType 服务类型
     * @returns 返回能力描述信息的 XML 字符串
     * @throws 当服务返回的能力描述信息验证失败时，抛出错误
     */
    async getCapabilities (serverUrl, serviceType) {
      const capabilitiesUrl = `${serverUrl.split(serviceType === 'WMTS' ? 'getTile' : 'getMap')[0]}getCapabilities`; // 根据serviceType动态选择分割方式
      const xmlResponse = await this.$get(capabilitiesUrl);
      const validateInfo = this.validateXML(xmlResponse);

      if (validateInfo.error_code === 1 || validateInfo.error_code === 2) {
        this.$message.error(`${serviceType}服务getCapabilities验证错误：` + xmlResponse)
        this.isloadLayer = false;
        throw new Error(`${serviceType}服务getCapabilities验证错误：` + xmlResponse);
      }

      return xmlResponse;
    },
    /*
    * 验证xml格式的正确性
    */
    validateXML (xmlContent) {
      //errorCode 0是xml正确，1是xml错误，2是无法验证
      var xmlDoc, errorMessage, errorCode = 0;
      // code for IE
      if (window.ActiveXObject) {
        xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
        xmlDoc.async = "false";
        xmlDoc.loadXML(xmlContent);

        if (xmlDoc.parseError.errorCode != 0) {
          errorMessage = "错误code: " + xmlDoc.parseError.errorCode + "\n";
          errorMessage = errorMessage + "错误原因: " + xmlDoc.parseError.reason;
          errorMessage = errorMessage + "错误位置: " + xmlDoc.parseError.line;
          errorCode = 1;
        }
        else {
          errorMessage = "格式正确";
        }
      }
      // code for Mozilla, Firefox, Opera, chrome, safari,etc.
      else if (document.implementation.createDocument) {
        var parser = new DOMParser();
        xmlDoc = parser.parseFromString(xmlContent, "text/xml");
        var error = xmlDoc.getElementsByTagName("parsererror");
        if (error.length > 0) {
          if (xmlDoc.documentElement.nodeName == "parsererror") {
            errorCode = 1;
            errorMessage = xmlDoc.documentElement.childNodes[0].nodeValue;
          } else {
            errorCode = 1;
            errorMessage = xmlDoc.getElementsByTagName("parsererror")[0].innerHTML;
          }
        }
        else {
          errorMessage = "格式正确";
        }
      }
      else {
        errorCode = 2;
        errorMessage = "浏览器不支持验证，无法验证xml正确性";
      }
      return {
        "msg": errorMessage,
        "error_code": errorCode
      };
    },
    // 从WMS的xml中获取中心点
    getWMSBoundingBox (capabilitiesXml) {
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(capabilitiesXml, "text/xml");
      const BoundingBox = xmlDoc.getElementsByTagName("BoundingBox")[0];
      const minx = parseFloat(BoundingBox.getAttribute('minx'))
      const miny = parseFloat(BoundingBox.getAttribute('miny'))
      const maxx = parseFloat(BoundingBox.getAttribute('maxx'))
      const maxy = parseFloat(BoundingBox.getAttribute('maxy'))
      const centerX = (minx + maxx) / 2
      const centerY = (miny + maxy) / 2
      return [centerX, centerY]
    },
    // 从WMTS的xml中获取中心点
    getWMTSBoundingBox (capabilitiesXml) {
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(capabilitiesXml, "text/xml");
      const BoundingBox = xmlDoc.getElementsByTagName("ows:BoundingBox")[0];
      let lons = []
      let lats = []
      for (let i = 0; i < BoundingBox.children.length; i++) {
        const cornerDom = BoundingBox.children[i]
        lons.push(parseFloat(cornerDom.innerHTML.split(' ')[0]))
        lats.push(parseFloat(cornerDom.innerHTML.split(' ')[1]))
      }
      const centerX = (lons[0] + lons[1]) / 2
      const centerY = (lats[0] + lats[1]) / 2
      return [centerX, centerY]
    },
    // 从XYZ的服务中获取中心点
    async getXYZCenter (serverIpPort, serverName, layerName) {
      const { label } = this.selectNode;
      if (label == '矢量数据') {
        const lengendData = await this.getLengend(serverIpPort, serverName, layerName);
        if (!lengendData) return null;
        try {
          const res = await this.$get(`${serverIpPort}/mapserver/${serverName}/${lengendData.id}/query?withGeometry=true`);
          if (res && res.length) {
            const dataItem = res[0];
            const { geom } = dataItem;
            const coordinates = this.dealwithGeom(geom);
            if (!coordinates.length) return;
            const pointList = coordinates.map(e => { return turf.point(e); });
            const features = turf.featureCollection(pointList);
            const center = turf.center(features).geometry.coordinates;
            if (!center || !center.length) return;
            return center
          }
        } catch (error) {
          console.log('获取矢量服务定位方法报错：', error);
        }
      } else {
        try {
          const res = await this.$get(`${serverIpPort}/rasterserver/image/getServiceBBox/${serverName}`);
          if (res && res.length) {
            const center = [(res[0] + res[2]) / 2, (res[1] + res[3]) / 2];
            return center
          }
        } catch (error) {
          console.log('获取影像服务定位方法报错：', error);
        }
      }
    },
    // 获取图层列表
    async getLengend (serverIpPort, serverName, layerName) {
      let lengendData = null;
      try {
        const res = await this.$get(`${serverIpPort}/mapserver/styleInfo/${serverName}/${layerName}/layer.json`);
        if (res && res.length) {
          lengendData = res[0];
        }
      } catch (error) {
        console.log('获取字段列表报错：', error);
      }
      return lengendData;
    },

    // 获取服务名称、图层名称
    getServerInfo (serverType, serverUrl) {
      const { label } = this.selectNode;
      let serverName = '';
      let layerName = '';
      switch (serverType) {
        case 'WMTS':
          const wmtsServerNameReg = new RegExp("(?<=/1.0/).*?(?=/)");
          serverName = serverUrl.match(wmtsServerNameReg) != null ? serverUrl.match(wmtsServerNameReg)[0] : '';
          const wmtsLayerNameReg = new RegExp(`(?<=/${serverName}/).*?(?=/)`);
          layerName = serverUrl.match(wmtsLayerNameReg) != null ? serverUrl.match(wmtsLayerNameReg)[0] : '';
          break;

        case 'WMS':
          const wmsServerNameReg = new RegExp("(?<=/1.1.1/).*?(?=/)");
          serverName = serverUrl.match(wmsServerNameReg) != null ? serverUrl.match(wmsServerNameReg)[0] : '';
          const wmsLayerNameReg = new RegExp(`(?<=/${serverName}/).*?(?=/)`);
          layerName = serverUrl.match(wmsLayerNameReg) != null ? serverUrl.match(wmsLayerNameReg)[0] : '';
          break;

        case 'XYZ':
          const typeReg = label == '矢量数据' ? new RegExp("(?<=/mapserver/).*?(?=/)") : new RegExp("(?<=/rasterserver/).*?(?=/)");
          const type = serverUrl.match(typeReg) != null ? serverUrl.match(typeReg)[0] : '';
          const mapServerNameReg = label == '矢量数据' ? new RegExp(`(?<=/${type}/).*(?=/)`) : new RegExp(`(?<=/${type}/).*?(?=/)`);
          const mapLayerNameReg = new RegExp("(?<=styleId=)[^&]*");
          serverName = serverUrl.match(mapServerNameReg) != null ? serverUrl.match(mapServerNameReg)[0] : '';
          layerName = serverUrl.match(mapLayerNameReg) != null ? serverUrl.match(mapLayerNameReg)[0] : '';
          break;

        default:
          break;
      }
      return { serverName, layerName };
    },

    // 处理 geom
    dealwithGeom (geom) {
      let geojson = wkt.parse(geom);
      let coordinates = [];
      switch (geojson.type) {
        case "MultiPolygon":
          coordinates = geojson.coordinates[0][0];
          break;

        case "Polygon":
          coordinates = geojson.coordinates[0];
          break;

        case "LineString":
        case "Point":
          coordinates = [geojson.coordinates];
          break;

        case "MultiLineString":
          coordinates = geojson.coordinates[0];
          break;

        default:
          break;
      }
      return coordinates;
    },

    // 获取高度
    getHeight (latLogList) {
      return new Promise(res => {
        const cesiumSamplePoints = latLogList.map(e => { return Cesium.Cartographic.fromDegrees(e[0], e[1]); });
        const promise = Cesium.sampleTerrain(this.viewer.terrainProvider, 11, cesiumSamplePoints);
        promise.then(data => {
          let positions = (data && data.length) ? data : [];
          res(positions[0].height);
        });
      });
    }
  },

  created () {
    this.viewer = this.Global.viewer;
    this._Layer = new Layer({ Vm: this, viewer: this.viewer });
  },

  beforeDestroy () {
    this.isloadLayer = false;
    this._Layer.destroy();
    this.clearLayerForm();
  }
}
</script>

<style lang="less" scoped>
.layer-dialog {
  .box-content {
    width: 50%;

    /deep/.el-form {
      .radio-type {
        width: 100%;

        .el-form-item__content {
          display: flex;
          justify-content: space-between;
          align-items: center;
          width: 100%;
          padding: 0 50px;
          margin: 0 !important;
          box-sizing: border-box;

          &::before,
          &::after {
            display: none;
          }

          .el-radio {
            margin-right: 0;
            font-size: 14px;
            color: #ffffff;
          }
        }
      }
    }

    /deep/.layer-server-box {
      position: relative;
      margin-bottom: 60px !important;

      .layer-url-hint {
        position: absolute;
        left: 0;
        top: 55px;
      }
    }
  }
}
</style>